/*******************************************************************************
*  file    : transform_dif.cpp
*  created : 23.06.2014
*  author  : Slyshyk Oleksiy (alexSlyshyk@gmail.com)
*******************************************************************************/

#include "transform_dif_heuristics.hpp"
#include <QDebug>
#include <algorithm>
#include <QVariantMap>

#include <utils.hpp>
#include <exception>


TransformDif::TransformDif(QObject *parent) :
    QObject(parent)
{

}

QString TransformDif::name() const
{
    return QStringLiteral("Dif_Heuristics");
}

NamedData *TransformDif::transform(const NamedData *data, const QVariant &params)
{
    QScopedPointer<plugin_utils::TransformParamsPrivate> data_;
    data_.reset(new plugin_utils::TransformParamsPrivate );
    data_->setParams(params);

    std::vector<std::complex<double> > sample = plugin_utils::read_simple_from_file<std::complex<double>>(data_->patternFileName());
    NamedData* res = new NamedData;
    res->name = data->name + "_dif";
    res->pantry["id"] = params.toMap()["id"];

    if(data_->freqCoef() == 0)
        data_->setSampleRate(1.0);

    int data_sz = data->data.size() * data_->freqCoef();

    res->sampleRate = data->sampleRate * data_->freqCoef();

    try
    {
        res->data = std::vector<std::complex<double>>(data_sz);
    }
    catch(std::bad_alloc& e)
    {
        qDebug() << e.what();
    }
    catch(...)
    {
        qDebug() << "Fuck !!!";
    }

    transform(sample, data, res, data_.data());

    // Нормализация
    plugin_utils::normalize(res->data, data_->normalization());

    return res;
}

QStringList TransformDif::patternList() const
{
    return plugin_utils::fileList(QStringLiteral("pattern*.dat"));
}

void TransformDif::transform(const std::vector<std::complex<double> > &sample, const NamedData *data,
                               NamedData *res, plugin_utils::TransformParamsPrivate* data_)
{
    double start_time = 0.000001;
    double dt = 1.0 / data_->freqCoef();
    double dt_data = 1.0 ;

    double d,s,fx_i;
    int sample_steps = sample.size() * data_->freqCoef();
    const auto &dat = data->data;
    double point_time;

    int iter_count = 0;
    double min_ansver = 1e+10;

    if(data_->prevTime() > 0.000001)
        {
            data_->setZoneBegin(data_->prevTime() - 0.00001);
            data_->setZoneEnd  (data_->prevTime() + 0.00001);
        }

    for(decltype(res->data.size()) idx_data = 0; idx_data < res->data.size() ; ++idx_data)
        {
            fx_i = 0;
            point_time = (idx_data)*(1.0 / res->sampleRate);
            if(!(point_time < data_->zoneBegin() || point_time > data_->zoneEnd()))
                {
                    for(int idx_sample = 0; idx_sample < sample_steps; ++idx_sample)
                        {
                            d = plugin_utils::get_func_value_linear(dat   ,start_time            ,dt_data,(idx_data+idx_sample)*dt);
                            s = plugin_utils::get_func_value_linear(sample,start_time+idx_data*dt,dt_data,(idx_data+idx_sample)*dt);
                            d -= data_->fxOffset();
                            s -= data_->fxOffset();
                            fx_i += std::abs(d - s) ;
                            iter_count++ ;
                            if(fx_i > min_ansver)
                                {
                                    fx_i = 1e+6;
                                    break;
                                }
                        }
                    if(fx_i < min_ansver)
                        min_ansver = fx_i;
                }

            res->data[idx_data] = fx_i;
            if(idx_data > 2)
                {
                    double dif_1 = res->data[idx_data-1].real() - res->data[idx_data-2].real();
                    double dif_2 = res->data[idx_data-0].real() - res->data[idx_data-1].real();
                    if(dif_1 < 0 && dif_2 > 0 )
                        {
                            for(int i = 0; i < 10*data_->freqCoef() ; ++i)
                                {
                                    idx_data+=1;
                                    res->data[idx_data] = 3e+6;
                                }
                        }
                }
         }



    auto st = std::adjacent_find(res->data.begin(),res->data.end(),
                                 [](const std::complex<double>& d1,const std::complex<double>& d2)->bool
                                    {return ((d1.real() == 0.) && (d2.real() > 0.));});
    if(st != res->data.end())
        st++;
    auto end = std::adjacent_find(st,res->data.end(),
                                 [](const std::complex<double>& d1,const std::complex<double>& d2)->bool
                                    {return ((d1.real() == 0.) && (d2.real() == 0.));});

    auto complex_less = [](std::complex<double>& pt1, std::complex<double>& pt2){ return (pt1.real() < pt2.real()) ;} ;
    auto max = std::max_element(st, end, complex_less);
    auto min = std::min_element(st, end, complex_less);

    data_->setMinIdx( std::distance(res->data.begin(),min) );
    data_->setMaxIdx( std::distance(res->data.begin(),max) );
    res->pantry["max_idx"] = data_->maxIdx();
    res->pantry["min_idx"] = data_->minIdx();
    res->pantry["ansver_idx"] = data_->minIdx();
    res->pantry["iter_count"] = iter_count ;
    qDebug() << "Iter count Heuristic " << iter_count;
}


